package net.cohoivang.web;

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.LinkedList;

public class OurConnection {
	private static LinkedList<OurConnection> pool = new LinkedList<OurConnection>();
	
	private static String dbUrl;
	private static int poolSize;
	
	private static int connectionCount;
	
	private Connection underlineConnection;
	private Connection underlineConnectionOutOfService;
	private final LinkedList<OurStatementBase> allStatements = new LinkedList<OurStatementBase>();
	
	static void initialize() throws SQLException {
		poolSize = Integer.parseInt(GoldenChance.getProperties("db_maxActive"));
		dbUrl = GoldenChance.getProperties("db_connection_string");
		if (dbUrl == null) {
			throw new SQLException("db_connection_string not found from properties file.");
		}
	}
	
	public OurStatement createOurStatement() {
		OurStatement stmt = new OurStatement(this);
		allStatements.add(stmt);
		return stmt;
	}
	
	public OurPrepareStatement createOurPrepareStatement(String sql) {
		OurPrepareStatement preStmt = new OurPrepareStatement(this, sql);
		allStatements.add(preStmt);
		return preStmt;
	}
	
	public OurPrepareStatement createOurPrepareStatement(String sql, int options) {
		OurPrepareStatement preStmt = new OurPrepareStatement(this, sql);
		allStatements.add(preStmt);
		return preStmt;
	}
	
	private void openConnectionIfNeed() throws SQLException {
		if (underlineConnection == null) {
			System.out.println("Opening a new database connection... " +
					"(Currently opening: " + connectionCount + ":" + pool.size() + ") / " + 
					dbUrl.substring(0, dbUrl.indexOf("password")));
			underlineConnection = DriverManager.getConnection(dbUrl);
		}
	}
	
	void reopenConnection() throws SQLException {
		System.out.println("Reopening jdbc connnection...");
		try {
			if (underlineConnection != null) {
				underlineConnection.close();
			}
		} finally {
			underlineConnection = null;
		}
	}
	public Statement createRealStatement() throws SQLException {
		openConnectionIfNeed();
		return underlineConnection.createStatement();
	}
	
	
	
	public PreparedStatement prepareRealStatement(String sql) throws SQLException {
		openConnectionIfNeed();
		return underlineConnection.prepareStatement(sql);
	}
	
	public PreparedStatement prepareRealStatement(String sql, Integer options) throws SQLException {
		openConnectionIfNeed();
		return underlineConnection.prepareStatement(sql, options);
	}
	
	private OurConnection() {
		connectionCount ++;
	}
	
	static OurConnection poolOut(String caller) {
		synchronized (pool) {
			while(pool.isEmpty()) {
				if (connectionCount < poolSize) {
					System.out.println("Create new connection " + connectionCount + " call by " + caller);
					return new OurConnection();
				}
				try {
					System.out.println("Exceed db pool size: " + poolSize + ", wait to try again... (" +
							connectionCount + "/" + pool.size() + ")");
					pool.wait();
				} catch (InterruptedException ex) {
					ex.printStackTrace();
				}
			}
			
			OurConnection oc = pool.removeFirst();
			oc.underlineConnection = oc.underlineConnectionOutOfService;
			oc.underlineConnectionOutOfService = null;
			System.out.println("Connection pulled-out: " + connectionCount + ", called by " + caller);
			return oc;
		}
	}
	
	public void close() {
		
		for (OurStatementBase s : allStatements) {
			try {
				s.close();
			} catch (SQLException e) {
				//ignore
			}
		}
		synchronized (pool) {
			//if (this.underlineConnection != null) {
				this.underlineConnectionOutOfService = underlineConnection;
				this.underlineConnection = null;
				
				//close may be called more than one, so check if contains
				if (!pool.contains(this)) {
					System.out.println("Connection pulled-in: " + connectionCount);
					pool.add(this);
				} else {
					//this could be the case of repeatedly close()
					System.out.println("Connection pulled-in (repeated): " + connectionCount);
				}
			//} else {
				
				//actually we never release any JnvConnection object
				//connectionCount--;
			//}
			pool.notifyAll();
		}
	}
	
	
}
